import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class GUI extends JFrame 
{
	private static final long serialVersionUID = 1L;
	private static final int FRAME_HEIGHT = 500;
	private static final int FRAME_WIDTH = 900;
	private static final int TEXTAREA_HEIGHT = 400;
	private static final int TEXTAREA_WIDTH = 500;

	private CPU my_cpu;
	private List<IODevice> my_device_list;
	private List<ProcessControlBlock> my_process_list;
	private List<SharedMemory> my_memory_list;
	private SystemTimer my_timer;

	private JTextArea my_text_area;
	private JScrollPane my_scroll_bar;
	private int my_number_processes;
	private JTextField my_processes_textfield;
	private String my_scheduling_policy;
	private JComboBox<String> my_schedules;
	private String my_types;
	private JComboBox<String> my_types_combobox;
	private JComboBox<String> my_priority_combobox;
	private JButton my_button;

	public GUI() 
	{
		super("Project 2 (OS Simulator)");
		setPreferredSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT));
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		Point middle = new Point(screenSize.width / 2, screenSize.height / 2);
		Point newLocation = new Point(middle.x - (FRAME_WIDTH / 2), 
		                              middle.y - (FRAME_HEIGHT / 2));
		
		this.setLocation(newLocation);

		createFields();
	}

	private final void createFields() 
	{
		my_device_list = new CopyOnWriteArrayList<IODevice>();
		my_process_list = new CopyOnWriteArrayList<ProcessControlBlock>();
		my_memory_list = new CopyOnWriteArrayList<SharedMemory>();
	}

	public void setup() 
	{
		add(createCenterPanel(), BorderLayout.CENTER);
		add(createSouthPanel(), BorderLayout.SOUTH);
		add(createEastPanel(), BorderLayout.EAST);

		pack();
		setVisible(true);
	}

	private JPanel createCenterPanel() 
	{
		final JPanel panel = new JPanel();

		my_text_area = new JTextArea();
		my_text_area.setEditable(false);
	
		my_text_area.setMargin(new Insets(10, 10, 10, 10));
		
		my_scroll_bar = new JScrollPane(my_text_area, 
				JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
				JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		my_scroll_bar.setPreferredSize(new Dimension(TEXTAREA_WIDTH,
				TEXTAREA_HEIGHT));
		panel.add(my_scroll_bar);
		
		return panel;
	}

	private JPanel createSouthPanel() 
	{
		final JPanel panel = new JPanel(new FlowLayout());

		final JButton button = new JButton("START"); //************************ Start button
		button.addActionListener(new ActionListener() 
		{
			public void actionPerformed(final ActionEvent the_event)
			{
				if (button.getText().equals("START"))
				{
					my_types_combobox.setEnabled(false);
					my_processes_textfield.setEnabled(false);
					my_button.setEnabled(false);
					
					my_cpu = new CPU((String) my_schedules.getSelectedItem(), my_process_list, 
										my_device_list, my_text_area, my_timer);
					
					for (final IODevice device : my_device_list)
					{
						new Thread(device).start();
					}
					new Thread(my_cpu).start();
					
					button.setText("STOP");
				}
				else
				{
					my_cpu.stop();
					for (final IODevice device : my_device_list)
					{
						device.stop();
					}
					my_text_area.append("\n\nTHE SIMULATION HAS STOPPED \n\n");
				}
			}
		});

		panel.add(button);

		return panel;
	}

	private JPanel createEastPanel() 
	{
		final JPanel wrapper = new JPanel();
		final JPanel panel1 = new JPanel(new GridLayout(16,0));

		wrapper.setLayout(new BorderLayout());
		
		final JLabel scheduling_label = new JLabel("SCHEDULING");
		scheduling_label.setHorizontalAlignment(JLabel.CENTER);
		panel1.add(scheduling_label);
		
		final JLabel policy_label = new JLabel("Select Scheduling Policy");
		policy_label.setHorizontalAlignment(JLabel.CENTER);
		panel1.add(policy_label);
		
		final JLabel priority_label = new JLabel("Select Process Priority \n (5 = highest, 1 = lowest)");
		priority_label.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10));
		priority_label.setHorizontalAlignment(JLabel.CENTER);
		priority_label.setVisible(false);
	//***************************************************** SELECT A PRIORITY ************************************//	
		my_priority_combobox = new JComboBox<String>(new String[]{ 
				"0 = lowest", "1", "2", "3", "4", "5 = highest"});
		my_priority_combobox = new JComboBox<String>(new String[]{
				"1 = lowest", "2", "3", "4", "5 = highest"});
		my_priority_combobox.setSelectedIndex(0);
		my_priority_combobox.setEditable(false);
		my_priority_combobox.setVisible(false);
		//******************************************************** SELECT A SCHEDULING POLICY *********************//
		my_schedules = new JComboBox<String>(new String[]{
				"Round-Robin", "Priority", "Lottery"});
		my_schedules.setSelectedIndex(0);
		panel1.add(my_schedules, BorderLayout.LINE_END);
		
		// ************************************************************* Policy button ****** 
		final JButton policy_button = new JButton("Select Policy");
		policy_button.addActionListener(new ActionListener()
		{
			public void actionPerformed(final ActionEvent the_event)
			{
				if (my_schedules.getSelectedItem().equals("Priority"))
				{
					my_priority_combobox.setVisible(true);
					priority_label.setVisible(true);
				}
				else
				{
					my_priority_combobox.setVisible(false);
					priority_label.setVisible(false);
				}
				
				policy_button.setEnabled(false);
				my_schedules.setEnabled(false);
				my_button.setEnabled(true);
			}
		});
		panel1.add(policy_button);
		
		panel1.add(new JSeparator());
		
		panel1.add(priority_label);
		panel1.add(my_priority_combobox);
		panel1.add(new JSeparator());
		
		final JLabel process_label = new JLabel("PROCESS");
		process_label.setHorizontalAlignment(JLabel.CENTER);
		panel1.add(process_label);
		
		final JLabel name_label = new JLabel("Number of Processes");
		name_label.setHorizontalAlignment(JLabel.CENTER);
		panel1.add(name_label);
		
		my_processes_textfield = new JTextField(10);//****************** text field how many process.
		panel1.add(my_processes_textfield, BorderLayout.PAGE_START);
		
		final JLabel type_label = new JLabel("Process Type");
		type_label.setHorizontalAlignment(JLabel.CENTER);
		panel1.add(type_label);
//******************************************************** PROCESS TYPES ************************//
		my_types_combobox = new JComboBox<String>(new String[]{
				"Keyboard", "Producer - Consumer (pair)", "Calculator"});
		my_types_combobox.setSelectedIndex(0);
		my_types_combobox.setEditable(false);
		panel1.add(my_types_combobox, BorderLayout.LINE_START);
		
		my_button = new JButton("Add Process"); //**************************** Button add process
		my_button.setEnabled(false);
		my_button.addActionListener(new ProcessesListeners());
		panel1.add(my_button, BorderLayout.PAGE_END);
		wrapper.add(panel1);

		return wrapper;
	}
	
	public String getSchedulingPolicy()
	{
		return my_scheduling_policy;
	}
	
	public String getTypes() 
	{
		return my_types;
	}
	
	public int getProcesses() 
	{
		return my_number_processes;
	}
	
	private class ProcessesListeners implements ActionListener 
	{
		@Override
		public void actionPerformed(final ActionEvent the_event)
		{
			my_number_processes = Integer.parseInt(my_processes_textfield.getText()); 
			my_scheduling_policy = (String) my_schedules.getSelectedItem();
			my_types = (String) my_types_combobox.getSelectedItem();
			
			AbstractProcess process;
			
			int priority;
			
			if (((String) my_schedules.getSelectedItem()).equals("Priority"))
			{
				priority = my_priority_combobox.getSelectedIndex();
			}
			else
			{
				priority = -1;
			}
			
			for (int i = 0; i < my_number_processes; i++)
			{
				ProcessControlBlock pcb;
				IODevice device;
				
				switch (my_types)
				{
					case "Keyboard":
						process = new UIProcess(my_process_list.size(), "UI", priority);
						device = new IODevice(my_device_list.size() + 1, my_types);
						pcb = new ProcessControlBlock("RUNNABLE", process);
						pcb.setDevice(device);
						device.setPCB(pcb);
						my_process_list.add(pcb);
						my_device_list.add(device);
						break;
					case "Producer - Consumer (pair)":
						final SharedMemory buffer = new SharedMemory(my_memory_list.size());
						process = new ProducerProcess(my_process_list.size(), "Producer", priority);
						pcb = new ProcessControlBlock("RUNNABLE", process);
						buffer.setProducer(pcb);
						((ProducerProcess) process).setBuffer(buffer);
						my_process_list.add(pcb);
						process = new ConsumerProcess(my_process_list.size(), "Consumer", priority);
						pcb = new ProcessControlBlock("RUNNABLE", process);
						buffer.setConsumer(pcb);
						((ConsumerProcess) process).setBuffer(buffer);
						my_process_list.add(pcb);
						my_memory_list.add(buffer);
						break;
					case "Calculator":
						process = new CalculatingProcess(my_process_list.size(), my_types, priority);
						device = new IODevice(my_device_list.size() + 1, "Auxiliary");
						pcb = new ProcessControlBlock("RUNNABLE", process);
						pcb.setDevice(device);
						device.setPCB(pcb);
						my_process_list.add(pcb);
						my_device_list.add(device);
						break;
				};
			}
			
			my_text_area.append("\n" + my_number_processes + " process");
			if (my_number_processes > 1)
			{
				my_text_area.append("es");
			}
			my_text_area.append(" created of type = ");
			my_text_area.append(my_types + " added to the list.\n");
			
			my_types_combobox.setSelectedIndex(0);
			my_priority_combobox.setSelectedIndex(0);
			my_processes_textfield.setText("");
		}
		
	}
}
